package com.kinggm.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Map;
import java.util.Properties;

/**
 * Mybatis分页插件 具体实现
 */

@Intercepts(@Signature(
        type = StatementHandler.class,
        method = "prepare",
        args = {Connection.class, Integer.class}
))
@Slf4j
@Component
public class MyPagePlugin implements Interceptor {


    /**
     * 核心业务
     * <p>
     * 1、拿到原始sql
     * 2、修改原始sql，增加分页
     * 3、执行jdbc查询总数
     */

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 从invocation获取statementHandler对象
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        BoundSql boundSql = statementHandler.getBoundSql();
        // 原始sql
        String sql = boundSql.getSql();
        log.info("原始sql===>>>{}", sql);

        // 获取分页参数
        Object parameterObject = boundSql.getParameterObject();

        // 获取MappedStatement对象 (对应是就是我们写sql的xml文件)
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        // 获取mapper接口中方法名称
        String methodName = mappedStatement.getId();
        // 拦截方法名为ByPage结尾的方法 只拦截涉及到分页的方法
        if (methodName.matches(".*ByPage$")) {
            Map<String, Object> parameterMap = (Map<String, Object>) parameterObject;
            PageInfo pageInfo = (PageInfo) parameterMap.get("pageInfo");
            // 页大小
            int pageSize = pageInfo.getPageSize();
            // 当前页数
            int currentPage = pageInfo.getCurrentPage();
            // 查总数
            String countSql = "select count(0) from (" + sql + ") t";
            log.info("查总数sql===>>>{}", countSql);

            // 执行jdbc操作
            Connection connection = (Connection) invocation.getArgs()[0];
            PreparedStatement countStatement = connection.prepareStatement(countSql);
            ParameterHandler parameterHandler = (ParameterHandler) metaObject.getValue("delegate.parameterHandler");
            parameterHandler.setParameters(countStatement);
            ResultSet resultSet = countStatement.executeQuery();
            if (resultSet.next()) {
                pageInfo.setTotalNumber(resultSet.getInt(1));
                resultSet.close();
                countStatement.close();
            }
            // 改造sql limit
            String pageSql = initPageSql(sql, pageInfo);
            log.info("分页sql===>>>{}", pageSql);
            metaObject.setValue("delegate.boundSql.sql", pageSql);
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

    /**
     * @param sql 原始sql
     * @param pageInfo 分页对象
     * @return 分页sql
     */
    private String initPageSql(String sql, PageInfo pageInfo) {
        int totalNumber = pageInfo.getTotalNumber();
        if (totalNumber <= 0) {
            return sql;
        }
        int pageSize = pageInfo.getPageSize();
        int currentPage = pageInfo.getCurrentPage();

        if (currentPage <= 0) {
            currentPage = 1;
        }
        if (pageSize <= 0) {
            pageSize = 10;
        }

        int offset = (currentPage - 1) * pageSize;

        StringBuilder sb = new StringBuilder();
        sb.append(sql).append(" limit ").append(offset).append(",").append(pageSize);
        return sb.toString();
    }
}
